﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Ufaev.Cryptography.Test
{
	/// <summary>
	/// Summary description for Gost28147Tests.
	/// </summary>
	[TestClass]
	public class Gost28147Tests
	{
		#region Fields

		private TestContext testContextInstance;

		#endregion Fields

		#region Properties

		/// <summary>
		/// Gets or sets the test context which provides
		/// information about and functionality for the current test run.
		///</summary>
		public TestContext TestContext
		{
			get { return testContextInstance; }
			set { testContextInstance = value; }
		}

		#endregion Properties

		#region Additional test attributes
		//
		// You can use the following additional attributes as you write your tests:
		//
		// Use ClassInitialize to run code before running the first test in the class
		// [ClassInitialize()]
		// public static void MyClassInitialize(TestContext testContext) { }
		//
		// Use ClassCleanup to run code after all tests in a class have run
		// [ClassCleanup()]
		// public static void MyClassCleanup() { }
		//
		// Use TestInitialize to run code before running each test 
		// [TestInitialize()]
		// public void MyTestInitialize() { }
		//
		// Use TestCleanup to run code after each test has run
		// [TestCleanup()]
		// public void MyTestCleanup() { }
		//
		#endregion

		#region Methods

		[TestMethod]
		public void Example1()
		{
			byte[] data = new byte[] {
				0xe3, 0x7d, 0x1c, 0x56,
				0x34, 0xc0, 0x15, 0x33
			};

			byte[] key = new byte[] {
				0x34, 0x87, 0x24, 0xa4,
				0xc1, 0xa6, 0x76, 0x67,
				0x15, 0x3d, 0xde, 0x59,
				0x33, 0x88, 0x42, 0x50,
				0xe3, 0x24, 0x8c, 0x65,
				0x7d, 0x41, 0x3b, 0x8c,
				0x1c, 0x9c, 0xa0, 0x9a,
				0x56, 0xd9, 0x68, 0xcf
			};

			Gost28147Managed gost28147Managed = new Gost28147Managed();
			gost28147Managed.GenerateIV();
			gost28147Managed.Key = key;

			gost28147Managed.Mode = CipherMode.ECB;
			gost28147Managed.Padding = PaddingMode.None;
			RunTest(gost28147Managed, data);

			gost28147Managed.Mode = CipherMode.CBC;
			gost28147Managed.Padding = PaddingMode.None;
			RunTest(gost28147Managed, data);

			gost28147Managed.Mode = CipherMode.CFB;
			gost28147Managed.Padding = PaddingMode.None;
			RunTest(gost28147Managed, data);


			gost28147Managed.Mode = CipherMode.ECB;
			gost28147Managed.Padding = PaddingMode.Zeros;
			RunTest(gost28147Managed, data);

			gost28147Managed.Mode = CipherMode.CBC;
			gost28147Managed.Padding = PaddingMode.Zeros;
			RunTest(gost28147Managed, data);

			gost28147Managed.Mode = CipherMode.CFB;
			gost28147Managed.Padding = PaddingMode.Zeros;
			RunTest(gost28147Managed, data);


			gost28147Managed.Mode = CipherMode.ECB;
			gost28147Managed.Padding = PaddingMode.ANSIX923;
			RunTest(gost28147Managed, data);

			gost28147Managed.Mode = CipherMode.CBC;
			gost28147Managed.Padding = PaddingMode.ANSIX923;
			RunTest(gost28147Managed, data);

			gost28147Managed.Mode = CipherMode.CFB;
			gost28147Managed.Padding = PaddingMode.ANSIX923;
			RunTest(gost28147Managed, data);


			gost28147Managed.Mode = CipherMode.ECB;
			gost28147Managed.Padding = PaddingMode.ISO10126;
			RunTest(gost28147Managed, data);

			gost28147Managed.Mode = CipherMode.CBC;
			gost28147Managed.Padding = PaddingMode.ISO10126;
			RunTest(gost28147Managed, data);

			gost28147Managed.Mode = CipherMode.CFB;
			gost28147Managed.Padding = PaddingMode.ISO10126;
			RunTest(gost28147Managed, data);


			gost28147Managed.Mode = CipherMode.ECB;
			gost28147Managed.Padding = PaddingMode.PKCS7;
			RunTest(gost28147Managed, data);

			gost28147Managed.Mode = CipherMode.CBC;
			gost28147Managed.Padding = PaddingMode.PKCS7;
			RunTest(gost28147Managed, data);

			gost28147Managed.Mode = CipherMode.CFB;
			gost28147Managed.Padding = PaddingMode.PKCS7;
			RunTest(gost28147Managed, data);
		}

		[TestMethod]
		public void Example2()
		{
			string text = "This is message, length=32";
			byte[] data = Encoding.GetEncoding(1251).GetBytes(text);

			Gost28147Managed gost28147Managed = new Gost28147Managed();
			gost28147Managed.GenerateKey();
			gost28147Managed.GenerateIV();

			gost28147Managed.Mode = CipherMode.ECB;
			gost28147Managed.Padding = PaddingMode.ANSIX923;
			RunTest(gost28147Managed, data);

			gost28147Managed.Mode = CipherMode.CBC;
			gost28147Managed.Padding = PaddingMode.ANSIX923;
			RunTest(gost28147Managed, data);

			gost28147Managed.Mode = CipherMode.CFB;
			gost28147Managed.Padding = PaddingMode.ANSIX923;
			RunTest(gost28147Managed, data);


			gost28147Managed.Mode = CipherMode.ECB;
			gost28147Managed.Padding = PaddingMode.ISO10126;
			RunTest(gost28147Managed, data);

			gost28147Managed.Mode = CipherMode.CBC;
			gost28147Managed.Padding = PaddingMode.ISO10126;
			RunTest(gost28147Managed, data);

			gost28147Managed.Mode = CipherMode.CFB;
			gost28147Managed.Padding = PaddingMode.ISO10126;
			RunTest(gost28147Managed, data);


			gost28147Managed.Mode = CipherMode.ECB;
			gost28147Managed.Padding = PaddingMode.PKCS7;
			RunTest(gost28147Managed, data);

			gost28147Managed.Mode = CipherMode.CBC;
			gost28147Managed.Padding = PaddingMode.PKCS7;
			RunTest(gost28147Managed, data);

			gost28147Managed.Mode = CipherMode.CFB;
			gost28147Managed.Padding = PaddingMode.PKCS7;
			RunTest(gost28147Managed, data);
		}

		private void RunTest(Gost28147Managed cipher, byte[] inputData)
		{
			RunTest(cipher, inputData, null, null);
		}

		private void RunTest(Gost28147Managed cipher, byte[] inputData, byte[] expectedEncryptedData, byte[] expectedDecryptedData)
		{
			byte[] buffer = new byte[64];

			byte[] encryptedData = null;
			using (MemoryStream sourceStream = new MemoryStream(inputData),
				encryptedStream = new MemoryStream())
			{
				using (CryptoStream cryptoStream = new CryptoStream(sourceStream, cipher.CreateEncryptor(), CryptoStreamMode.Read))
				{
					int count;
					while ((count = cryptoStream.Read(buffer, 0, buffer.Length)) != 0)
					{
						encryptedStream.Write(buffer, 0, count);
					}
				}

				encryptedData = encryptedStream.ToArray();
			}

			if (expectedEncryptedData != null)
				CollectionAssert.AreEqual(expectedEncryptedData, encryptedData);

			byte[] decryptedData = null;
			using (MemoryStream sourceStream = new MemoryStream(encryptedData),
				decryptedStream = new MemoryStream())
			{
				using (CryptoStream cryptoStream = new CryptoStream(sourceStream, cipher.CreateDecryptor(), CryptoStreamMode.Read))
				{
					int count;
					while ((count = cryptoStream.Read(buffer, 0, buffer.Length)) != 0)
					{
						decryptedStream.Write(buffer, 0, count);
					}
				}

				decryptedData = decryptedStream.ToArray();
			}

			if (expectedDecryptedData != null)
				CollectionAssert.AreEqual(expectedDecryptedData, decryptedData);
			else
				CollectionAssert.AreEqual(inputData, decryptedData);
		}

		#endregion Methods
	}
}
